#include "StdAfx.h"
#include "Billboard.h"

using namespace std;

namespace FEngine
{
	Billboard::Billboard(Vector3 _pos, Vector3 _scale, const string& _textureName, float _windForce, float _windOffset)
		: Node(), mBillboardPosition(_pos), mBillboardScale(_scale), mWindForce(_windForce), mWindOffset (_windOffset)
	{
		mVertexBuffer		= NULL;
		mVertexDeclaration	= NULL;
		mIndexBuffer		= NULL;
		mMeshTexture		= NULL;
		mIsTexturePreLoaded	= false;
		
		_createGeometry();
		_loadTexture(_textureName);
	}
	
	Billboard::Billboard(Vector3 _pos, Vector3 _scale, LPDIRECT3DTEXTURE9 _texture, float _windForce, float _windOffset)
		: Node(), mBillboardPosition(_pos), mBillboardScale(_scale), mWindForce(_windForce), mWindOffset (_windOffset)
	{
		mVertexBuffer		= NULL;
		mVertexDeclaration	= NULL;
		mIndexBuffer		= NULL;
		mMeshTexture		= _texture;
		mIsTexturePreLoaded	= true;
		
		_createGeometry();
	}

	Billboard::~Billboard()
	{
		if (mVertexBuffer)
			mVertexBuffer->Release();
		if (mVertexDeclaration)
			mVertexDeclaration->Release();
		if (mIndexBuffer)
			mIndexBuffer->Release();
		if (!mIsTexturePreLoaded && mMeshTexture)
			mMeshTexture->Release();
	}

	void Billboard::_createGeometry()
	{
		// Create the quad (4 vertices)
		mNumberVertices = 4;
		unsigned int verticesSize = mNumberVertices * sizeof(FE_BILLBOARD);
		FE_BILLBOARD vertices[] = 
		{
			//	Local vertex position		Quad world position		Texture coordinate		Wind Force / Offset
			{ Vector3(-1.0f, -1.0f, 0),		Vector3(0, 0, 0),		Vector2(0, 1.0f),		Vector2(0, 0) },						// Bottom Left
			{ Vector3(-1.0f, 1.0f, 0),		Vector3(0, 0, 0),		Vector2(0, 0),			Vector2(mWindForce, mWindOffset) },		// Top Left
			{ Vector3(1.0f, 1.0f, 0),		Vector3(0, 0, 0),		Vector2(1.0f, 0),		Vector2(mWindForce, mWindOffset) },		// Top Right
			{ Vector3(1.0f, -1.0f, 0),		Vector3(0, 0, 0),		Vector2(1.0f, 1.0f),	Vector2(0, 0) },						// Bottom Right
		};

		// Scale the quad
		for (int i = 0; i < 4; ++i)
		{
			vertices[i].POSITION.x *= mBillboardScale.x;
			vertices[i].POSITION.y *= mBillboardScale.y;
			vertices[i].POSITION.z *= mBillboardScale.z;
		}

		// Set the wolrd position of the quad to the bottom
		mBillboardPosition.y += (vertices[1].POSITION.y - vertices[0].POSITION.y) / 2.0f;

		// Set the world position of each vertices
		for (int i = 0; i < 4; ++i)
			vertices[i].BILLBOARD_POSITION = mBillboardPosition;
		
		// Create the index
		mNumberPrimitives = 2;
		unsigned int indexesSize = 6 * sizeof(short);
		short indexes[] =
		{
			0, 1, 2, 0, 2, 3,
		};
		
		
		// Create the vertex buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexBuffer(verticesSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &mVertexBuffer, NULL);
		
		// Create the index buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateIndexBuffer(indexesSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIndexBuffer, NULL);


		VOID* pVoid;
		
		// Copy the vertex buffer data into memory
		mVertexBuffer->Lock(0, 0, (void**)&pVoid, 0);
		memcpy(pVoid, vertices, verticesSize);
		mVertexBuffer->Unlock();
		
		// Copy the index buffer data into memory
		mIndexBuffer->Lock(0, 0, (void**)&pVoid, 0);
		memcpy(pVoid, indexes, indexesSize);
		mIndexBuffer->Unlock();
		
		// Load the vertex declaration
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexDeclaration(VertexDeclarationBillboard, &mVertexDeclaration);
	}

	void Billboard::_loadTexture(const string& _textureName)
	{
		std::wstring stemp = std::wstring(_textureName.begin(), _textureName.end());
		LPCWSTR fileName = stemp.c_str();

		if (D3DXCreateTextureFromFileEx(CoreEngine::getInstance()->getDeviceInterface(), fileName, D3DX_DEFAULT, D3DX_DEFAULT, 
									D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 
									NULL, NULL, &mMeshTexture) != D3D_OK)
			throw FEngineException("MeshObject", "render", "Cannot load the texture file");
	}
	
	void Billboard::update(double _elapsedTime)
	{
		Node::update(_elapsedTime);
	}

	void Billboard::render(double _elapsedTime)
	{
		Node::render(_elapsedTime);

		LPD3DXEFFECT shader = CoreEngine::getInstance()->getShaderEffect();
		IDirect3DDevice9* device = CoreEngine::getInstance()->getDeviceInterface();
		
		D3DXMATRIX matVP = CoreEngine::getInstance()->getCamera()->_getMatrixView() * CoreEngine::getInstance()->getCamera()->_getMatrixProjection();

		if (shader->SetTechnique(shader->GetTechniqueByName("Billboard")) != D3D_OK)
			throw FEngineException("Billboard", "render", "Unknown shader technique");
		if (shader->SetMatrix(shader->GetParameterByName(NULL, "gViewProj"), &matVP) != D3D_OK)
			throw FEngineException("Billboard", "render", "Unknown shader parameter");
		if (shader->SetTexture(shader->GetParameterByName(NULL, "gMeshTexture"), mMeshTexture) != D3D_OK)
			throw FEngineException("MeshObject", "render", "Unknown shader parameter");
		shader->CommitChanges();

		device->SetVertexDeclaration(mVertexDeclaration);
		device->SetStreamSource(0, mVertexBuffer, 0, sizeof(FE_BILLBOARD));
		device->SetIndices(mIndexBuffer);
		
		unsigned int cPasses, iPass;
		shader->Begin(&cPasses, 0);
		for (iPass= 0; iPass< cPasses; ++iPass)
		{
			shader->BeginPass(iPass);
			CoreEngine::getInstance()->getDeviceInterface()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mNumberVertices, 0, mNumberPrimitives);
			shader->EndPass();
		}
		shader->End();
	}


} // namespace FEngine
